/**
 * Copyright (c) 2011-2013, kidzhou 周磊 (zhouleib1412@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jfinal.ext.plugin.redis;

import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;


import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;

import com.jfinal.ext.kit.ResourceKit;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.IPlugin;

public class JedisPlugin implements IPlugin {

    public JedisPool pool;
    private String config = "RedisConnector.properties";

    private String host = "localhost";
    private int port = 6379;
    private int timeout = 2000;
    private String password;
    
    /**
     * 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
     */
    private boolean blockwhenexhausted = true;
    /**
     * 是否启用pool的jmx管理功能, 默认true
     */
    private boolean jmxenabled = true;
    
    /**
     * JMX命名前缀，默认为pool
     */
    private String jmxnameprefix = "pool";
    /**
     * 是否启用后进先出, 默认true
     */
    private boolean lifo = true;
    /**
     * 最大空闲连接数, 默认8个
     */
    private int maxidle = 8;
    /**
     * 最大连接数, 默认8个
     */
    private int maxtotal = 8;
    
    /**
     * 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1
     */
    private long maxwaitmillis = -1;
    
    /**
     * 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
     */
    private long minevictableidletimemillis = 1800000;
    /**
     * 最小空闲连接数, 默认0
     */
    private int minidle = 0;
    /**
     * 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
     */
    private int numtestsperevictionrun = 3;
    /**
     * 对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断  (默认逐出策略) 
     */
    private long softminevictableidletimemillis = 1800000;
    /**
     * 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
     */
    private long timebetweenevictionrunsmillis = -1;
    /**
     * 在空闲时检查有效性, 默认false
     */
    private boolean testwhileidle = false;
    /**
     * 在返回时检查有效性, 默认false
     */
    private boolean testonreturn = false;
    /**
     * 在获取连接的时候检查有效性, 默认false
     */
    private boolean testonborrow = false;
    
    public JedisPlugin() {
    }

    public JedisPlugin(String host) {
        this.host = host;
    }

    public JedisPlugin(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public JedisPlugin(String host, int port, int timeout) {
        this.host = host;
        this.port = port;
        this.timeout = timeout;
    }

    public JedisPlugin config(String config) {
        this.config = config;
        return this;
    }

    @Override
    public boolean start() {
        Map<String, String> map = ResourceKit.readProperties(config);
        Set<Entry<String, String>> entrySet = map.entrySet();
        for (Entry<String, String> entry : entrySet) {
            parseSetting(entry.getKey(), entry.getValue().trim());
        }
        JedisShardInfo shardInfo = new JedisShardInfo(host, port, timeout);
        if (StrKit.notBlank(password)) {
            shardInfo.setPassword(password);
        }
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        setPoolConfig(poolConfig);
        pool = new JedisPool(poolConfig, shardInfo.getHost(), shardInfo.getPort(), shardInfo.getSoTimeout(),shardInfo.getPassword());
        JedisKit.init(pool);
        return true;
    }

    private void setPoolConfig(JedisPoolConfig poolConfig) {
    	poolConfig.setBlockWhenExhausted(blockwhenexhausted);
    	poolConfig.setJmxEnabled(jmxenabled);
    	poolConfig.setJmxNamePrefix(jmxnameprefix);
    	poolConfig.setLifo(lifo);
        poolConfig.setMaxIdle(maxidle);
        poolConfig.setMaxTotal(maxtotal);
        poolConfig.setMaxWaitMillis(maxwaitmillis);
        poolConfig.setMinEvictableIdleTimeMillis(minevictableidletimemillis);
        poolConfig.setMinIdle(minidle);
        poolConfig.setNumTestsPerEvictionRun(numtestsperevictionrun);
        poolConfig.setSoftMinEvictableIdleTimeMillis(softminevictableidletimemillis);
        poolConfig.setTimeBetweenEvictionRunsMillis(timebetweenevictionrunsmillis);
        poolConfig.setTestWhileIdle(testwhileidle);
        poolConfig.setTestOnReturn(testonreturn);;
        poolConfig.setTestOnBorrow(testonborrow);

    }

    @Override
    public boolean stop() {
        try {
            pool.destroy();
        } catch (Exception ex) {
            System.err.println("Cannot properly close Jedis pool:" + ex);
        }
        pool = null;
        return true;
    }

    private void parseSetting(String key, String value) {
    	if ("host".equalsIgnoreCase(key)) {
            host = value;
        } else if ("port".equalsIgnoreCase(key)) {
        	port = Integer.valueOf(port);
        } else if ("timeout".equalsIgnoreCase(key)) {
            timeout = Integer.valueOf(value);
        } else if ("password".equalsIgnoreCase(key)) {
            password = value;
        } else if ("blockwhenexhausted".equalsIgnoreCase(key)) {
        	blockwhenexhausted = Boolean.getBoolean(value);
        } else if ("jmxenabled".equalsIgnoreCase(key)) {
        	jmxenabled = Boolean.getBoolean(value);
        } else if ("jmxnameprefix".equalsIgnoreCase(key)) {
        	jmxnameprefix = value;
        } else if ("lifo".equalsIgnoreCase(key)) {
        	lifo = Boolean.getBoolean(value);
        } else if ("maxidle".equalsIgnoreCase(key)) {
            maxidle = Integer.valueOf(value);
        } else if ("maxtotal".equalsIgnoreCase(key)) {
        	maxtotal = Integer.valueOf(value);
        } else if ("maxwaitmillis".equalsIgnoreCase(key)) {
        	maxwaitmillis = Long.valueOf(value);
        } else if ("minevictableidletimemillis".equalsIgnoreCase(key)) {
            minevictableidletimemillis = Long.valueOf(value);
        } else if ("minidle".equalsIgnoreCase(key)) {
        	minidle = Integer.valueOf(value);
        } else if ("numtestsperevictionrun".equalsIgnoreCase(key)) {
            numtestsperevictionrun = Integer.valueOf(value);
        } else if ("softminevictableidletimemillis".equalsIgnoreCase(key)) {
            softminevictableidletimemillis = Long.valueOf(value);
        } else if ("timebetweenevictionrunsmillis".equalsIgnoreCase(key)) {
            timebetweenevictionrunsmillis = Long.valueOf(value);
        } else if ("testwhileidle".equalsIgnoreCase(key)) {
            testwhileidle = Boolean.getBoolean(value);
        } else if ("testonreturn".equalsIgnoreCase(key)) {
            testonreturn = Boolean.getBoolean(value);
        } else if ("testonborrow".equalsIgnoreCase(key)) {
            testonborrow = Boolean.getBoolean(value);
        }
    }
}
